#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define TSHM_CHAR_LEN sizeof(unsigned char)
#define TSHM_INT_LEN sizeof(unsigned int)
#define TSHM_STR_LEN 64
#define TSHM_CHAR_START_NUM 0
#define TSHM_INT_START_NUM 10000
#define TSHM_STR_START_NUM 20000
#define MAKE_CHAR_MIB(name) unsigned char name
#define MAKE_INT_MIB(name) unsigned int name
#define MAKE_STR_MIB(name) unsigned char name[TSHM_STR_LEN]
#define TSHM_KEY 2222
#define TSHM_BLOCK_LEN 40960
#define TSHM_SEM_KEY 2222

enum{
	/* Add char mib here[1] */
	ID_char_mib1=TSHM_CHAR_START_NUM+1,
	ID_char_mib2,
	ID_char_mib3
};
unsigned char *TshmcharTbl[]=
{
	/* Add char mib here[2] */
	"char_start",/* Don`t modify */
	"char_mib1",
	"char_mib2",
	"char_mib3",
	NULL
};

enum{
	/* Add int mib here[1] */
	ID_int_mib1=TSHM_INT_START_NUM+1,
	ID_int_mib2
};
unsigned char *TshmintTbl[]=
{
	/* Add int mib here[2] */
	"int_start",/* Don`t modify */
	"int_mib1",
	"int_mib2",
	NULL
};

enum{
	/* Add str mib here[1] */
	ID_str_mib1=TSHM_STR_START_NUM+1,
	ID_str_mib2
};
unsigned char *TshmstrTbl[]=
{
	/* Add str mib here[2] */
	"str_start",/* Don`t modify */
	"str_mib1",
	"str_mib2",
	NULL
};

typedef struct _data{
	/* Add char mib here[3] */
	MAKE_CHAR_MIB(char_start);
	MAKE_CHAR_MIB(char_mib1);
	MAKE_CHAR_MIB(char_mib2);
	MAKE_CHAR_MIB(char_mib3);
	
	/* Add int mib here[3] */
	MAKE_INT_MIB(int_start);
	MAKE_INT_MIB(int_mib1);
	MAKE_INT_MIB(int_mib2);
	
	/* Add str mib here[3] */
	MAKE_STR_MIB(str_start);
	MAKE_STR_MIB(str_mib1);
	MAKE_STR_MIB(str_mib2);
	
}TSHM_T;

TSHM_T *Tshmaddr=NULL;
int Tshm_sem_id;
union semun
{
  int val;
  struct semid_ds *buf;
  unsigned short *array;
}Tshm_sem_union;

void Tshm_init(void);
int Tshm_set(unsigned int id,void *value);
int Tshm_get(unsigned int id,void *value);
void Tshm_wipe(void);
void Tshm_dumpall(void);
void Tshm_sem_init(int key);
int Tshm_sem_p(void);
int Tshm_sem_v(void);

void Tshm_init(void)
{
	int shmid;
	struct shmid_ds buf;
	
	shmid = shmget(TSHM_KEY,TSHM_BLOCK_LEN,IPC_CREAT|0666);
	Tshmaddr = (TSHM_T *)shmat(shmid,NULL,0);
	//printf("Tshm_init() at 0x%p,size %d,real struct size %d\n",Tshmaddr,size,sizeof(struct __data));
	Tshm_sem_init(TSHM_SEM_KEY);
}

int Tshm_set(unsigned int id,void *value)
{
	if(!Tshmaddr){
		printf("Tshmaddr point error\n");
		return -1;
	}
	
	Tshm_sem_p();
	if(id>=TSHM_CHAR_START_NUM && id <TSHM_INT_START_NUM){
		memcpy((void *)&Tshmaddr->char_start + id * TSHM_CHAR_LEN, value, TSHM_CHAR_LEN);
	}
	else if(id>=TSHM_INT_START_NUM && id <TSHM_STR_START_NUM){
		memcpy((void *)&Tshmaddr->int_start + (id-TSHM_INT_START_NUM) * TSHM_INT_LEN, value, TSHM_INT_LEN);
	}
	else if(id>=TSHM_STR_START_NUM){
		strncpy((void *)Tshmaddr->str_start + (id-TSHM_STR_START_NUM) * TSHM_STR_LEN, value, TSHM_STR_LEN-1);
	}
	else{
		printf("Tshm_set id:%d fail\n",id);
		Tshm_sem_v();
		return -1;
	}
	Tshm_sem_v();
	
	return 1;
}

int Tshm_get(unsigned int id,void *value)
{
	if(!Tshmaddr){
		printf("Tshmaddr point error\n");
		return -1;
	}
	
	if(id>=TSHM_CHAR_START_NUM && id <TSHM_INT_START_NUM){
		memcpy(value,(void *)&Tshmaddr->char_start + id * TSHM_CHAR_LEN, TSHM_CHAR_LEN);
	}
	else if(id>=TSHM_INT_START_NUM && id <TSHM_STR_START_NUM){
		memcpy(value, (void *)&Tshmaddr->int_start + (id-TSHM_INT_START_NUM) * TSHM_INT_LEN, TSHM_INT_LEN);
	}
	else if(id>=TSHM_STR_START_NUM){
		strncpy(value,(void *)Tshmaddr->str_start + (id-TSHM_STR_START_NUM) * TSHM_STR_LEN, TSHM_STR_LEN);
	}
	else{
		printf("Tshm_get id:%d fail\n",id);
		return -1;
	}
	
	return 1;
}

void Tshm_wipe(void)
{
	Tshm_sem_p();
	memset(Tshmaddr,0,sizeof(TSHM_T));
	Tshm_sem_v();
}

void Tshm_dumpall(void)
{
	int i;
	
	printf("===== 1Byte =====\n");
	for(i=1;TshmcharTbl[i]!=NULL;i++){
		printf("%s=%d\n",TshmcharTbl[i],*(&Tshmaddr->char_start+i*TSHM_CHAR_LEN));
	}
	printf("===== 4Byte =====\n");
	for(i=1;TshmintTbl[i]!=NULL;i++){
		unsigned int intval;
		memcpy((void *)&intval,(void *)&Tshmaddr->int_start+i*TSHM_INT_LEN,TSHM_INT_LEN);
		printf("%s=%d\n",TshmintTbl[i],intval);
	}
	printf("===== String =====\n");
	for(i=1;TshmstrTbl[i]!=NULL;i++){
		printf("%s=%s\n",TshmstrTbl[i],Tshmaddr->str_start+i*TSHM_STR_LEN);
	}
}

unsigned int converName2Id(char *type,char *name)
{
	int i;
	
	if(!strcmp(type,"char")){
		for(i=0;TshmcharTbl[i]!=NULL;i++){
			if(!strcmp(name,TshmcharTbl[i]))
				return (TSHM_CHAR_START_NUM+i);
		}
	}
	else if(!strcmp(type,"int")){
		for(i=0;TshmintTbl[i]!=NULL;i++){
			if(!strcmp(name,TshmintTbl[i]))
				return (TSHM_INT_START_NUM+i);
		}
	}
	else if(!strcmp(type,"str")){
		for(i=0;TshmstrTbl[i]!=NULL;i++){
			if(!strcmp(name,TshmstrTbl[i]))
				return (TSHM_STR_START_NUM+i);
		}
	}
	
	return -1;
}

void Tshm_sem_init(int key)
{
	Tshm_sem_id = semget((key_t)key,1,0666 | IPC_CREAT);
	Tshm_sem_union.val=1;
	if (semctl(Tshm_sem_id,0,SETVAL,Tshm_sem_union)== -1)
    return;
}

int Tshm_sem_p(void)
{
	struct sembuf sem_b;
	sem_b.sem_num = 0;
  sem_b.sem_op = -1;
  sem_b.sem_flg = SEM_UNDO; //or IPC_NOWAIT, don't wait if unsuccessful
  if (semop(Tshm_sem_id,&sem_b,1) == -1)
		exit(EXIT_FAILURE);
  return 1;
}

int Tshm_sem_v(void)
{
	struct sembuf sem_b;
  sem_b.sem_num = 0;
  sem_b.sem_op = 1;
  sem_b.sem_flg = SEM_UNDO; //or IPC_NOWAIT, don't wait if unsuccessful
  if (semop(Tshm_sem_id,&sem_b,1) == -1)
		exit(EXIT_FAILURE);
  return 1;
}
